﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using Medianamik.Core;
using Medianamik.Core.DAL;
using Medianamik.Core.DAL.Repositories;
using Medianamik.Core.Security;
using Medianamik.Core.Sugar;
using Medianamik.UI.Web.MVP.View;
using StructureMap;
using SubSonic;

namespace Medianamik.UI.Web.MVP.Presentation
{
    public abstract class ModulePresenterWithRepository<TItem, TView, TRepository,
        TNodeTypeRepository, TMedianamikDTOWithNameRepository> :
        ModulePresenter<TView>
        where TItem : class, IMedianamikDTO
        where TView : IModuleViewWithRepository
        where TRepository : class, IMedianamikRepository<TItem>
        where TNodeTypeRepository : class, INodeTypeRepository
        where TMedianamikDTOWithNameRepository : class, IMedianamikRepository<IMedianamikDTOWithName>
    {
        protected ModulePresenterWithRepository(TView view,
            TRepository repository, TNodeTypeRepository nodeTypeRepository,
            TMedianamikDTOWithNameRepository baseTypeRepository)
            : base(view)
        {
            _nodeTypeRepository = nodeTypeRepository;
            _medianamikDTOWithNameRepository = baseTypeRepository;
            _repository = repository ?? ObjectFactory.GetInstance<TRepository>();
        }

        public TMedianamikDTOWithNameRepository MedianamikDTOWithNameRepository
        {
            get { return _medianamikDTOWithNameRepository; }
        }

        protected override void Initialize(object sender, EventArgs e)
        {
            View.PageIndexChanged += PageIndexChanged;
            View.PageSizeChanged += PageSizeChanged;
            View.LockRequestFailed += LockRequestFailed;
            View.ActivationStatusChanged += ActivationStatusChanged;
            View.OrderChanged += OrderChanged;
            View.CultureChanged += CultureChanged;
            View.NodeDeleted += NodeDeleted;
            View.Filter += Rebind;

            if (!View.IsPostBack)
            {
                View.PageIndex = PageIndex;
                View.PageSize = PageSize;
                View.Keywords = Keywords;
                View.SelectedStartDateTime = Start;
                View.SelectedEndDateTime = End;
                View.ModuleNodeId = ModuleNodeId;

                View.ReturnUrl = IsRoot ?
                    View.ListingPageUrl : GetReturnUrlFromQueryString().UrlDecodeBase64();

                if (View.Filters.HasFilter(ModuleViewFilters.Status))
                {
                    BindStatus(Status);
                }

                Bind();
                BindBreadcrumbTrail();

                if (View.AddButton != null)
                {
                    View.AddButton.NavigateUrl = AddUrl;
                }
            }

            if (View.AddButton != null && HasMultipleTypes)
            {
                View.AddTypeSelectorWindow();
            }

            View.AddDeleteNodeWindow();
        }

        private bool? _isRoot;
        protected virtual bool IsRoot
        {
            get { return _isRoot ?? (_isRoot = GetIsRoot()).GetValueOrDefault(true); }
        }

        protected virtual bool GetIsRoot()
        {
            return String.IsNullOrEmpty(GetReturnUrlFromQueryString());
        }

        private readonly TRepository _repository;
        public TRepository Repository
        {
            get { return _repository; }
        }

        private readonly TNodeTypeRepository _nodeTypeRepository;
        private readonly TMedianamikDTOWithNameRepository _medianamikDTOWithNameRepository;

        public TNodeTypeRepository NodeTypeRepository
        {
            get { return _nodeTypeRepository; }
        }

        private DateTime? _start;
        protected virtual DateTime? Start
        {
            get
            {
                return _start ?? (_start = GetStart() ?? DefaultStart);
            }
        }
        protected virtual DateTime? GetStart()
        {
            return View.IsPostBack
                       ? View.SelectedStartDateTime
                       :
                           View.QueryString.GetValue<long?>("start") == null
                               ? default(DateTime?)
                               :
                                   new DateTime(View.QueryString.GetValue<long?>("start").Value);
        }
        protected virtual DateTime? DefaultStart
        {
            get
            {
                return null;
            }
        }

        private int? _pageIndex;
        protected virtual int PageIndex
        {
            get
            {
                return _pageIndex ?? (_pageIndex = GetPageIndex()).GetValueOrDefault(1);
            }
            set { _pageIndex = value; }
        }

        protected virtual int? GetPageIndex()
        {
            return View.IsPostBack ? View.PageIndex : View.QueryString.GetValue<int?>("pageindex");
        }

        private int? _pageSize;
        protected virtual int PageSize
        {
            get
            {
                return _pageSize ?? (_pageSize = GetPageSize()).GetValueOrDefault(DefaultPageSize);
            }
            set { _pageSize = value; }
        }

        protected virtual int? GetPageSize()
        {
            return View.IsPostBack ? View.PageSize : View.QueryString.GetValue<int?>("pagesize");
        }

        protected virtual int DefaultPageSize
        {
            get
            {
                return 10;
            }
        }

        private DateTime? _end;
        protected virtual DateTime? End
        {
            get
            {
                return _end ?? (_end = GetEnd() ?? DefaultEnd);
            }
        }
        protected virtual DateTime? GetEnd()
        {
            return View.IsPostBack
                       ? View.SelectedEndDateTime
                       :
                           View.QueryString.GetValue<long?>("end") == null
                               ? default(DateTime?)
                               :
                                   new DateTime(View.QueryString.GetValue<long?>("end").Value);
        }
        protected virtual DateTime? DefaultEnd
        {
            get
            {
                return null;
            }
        }

        private string _keywords;
        protected virtual
        string Keywords
        {
            get
            {
                return _keywords ?? (_keywords = GetKeywords()) ?? DefaultKeywords;
            }
        }
        protected virtual string GetKeywords()
        {
            return View.IsPostBack ? View.Keywords : View.QueryString.GetValue<string>("keywords");
        }
        protected virtual string DefaultKeywords
        {
            get
            {
                return "";
            }
        }

        protected virtual bool HasMultipleTypes
        {
            get
            {
                return !TypeSelectorUrl.IsNullOrEmptyTrim();
            }
        }

        protected virtual void NodeDeleted(object sender, EventArgs e)
        {
            Bind();
        }

        protected virtual void LockRequestFailed(object sender, IdEventArgs e)
        {
            if (e.Id.HasValue && e.Id.Value != Guid.Empty)
                View.ShowLockInfoTooltip(e.Id.Value);

            Bind();
        }

        protected virtual void ActivationStatusChanged(object sender, ActivationStatusChangedEventArgs e)
        {
            if (e.Id.HasValue && e.Id.Value != Guid.Empty)
                Repository.SetActiveStatus(e.Id.Value, e.Culture, e.Status);

            Bind();
        }

        protected virtual void OrderChanged(object sender, OrderChangedEventArgs e)
        {
            if (e.Id.HasValue)
            {
                if (Roles.UserHasRight(e.Id.Value, SecurityLevels.Update))
                {
                    Repository.Reorder(e.Id.Value, e.DestinationId, e.DropPosition);
                    Bind();
                }
                else
                {
                    View.Alert("Medianamik_ContentManagement", "UserMayMoveNodePermissionAlert");
                }
            }
        }

        protected virtual string AddUrl
        {
            get
            {
                var returnUrl = String.IsNullOrEmpty(TypeSelectorUrl) ? View.EditPageUrl
                    .AddParam(DefaultParamNames.TypeID, BaseTypeId)
                    .AddParam(DefaultParamNames.ReturnURL, EditReturnURL.UrlEncodeBase64())
                    .AddParam(DefaultParamNames.HideTreeview, true) : TypeSelectorUrl;

                if (ParentNodeId.HasValue && ParentNodeId.Value != Guid.Empty)
                    returnUrl = returnUrl.AddParam(DefaultParamNames.ParentNodeID, ParentNodeId);

                return returnUrl;
            }
        }

        protected virtual Guid? ParentNodeId
        {
            get { return View.ParentNodeId; }
        }

        protected virtual string TypeSelectorUrl
        {
            get { return View.TypeSelectorUrl; }
        }

        protected virtual Guid ModuleNodeId
        {
            get { return View.ModuleNodeId; }
        }

        protected virtual Guid BaseTypeId
        {
            get { return View.BaseTypeId; }
        }
        protected virtual bool IsMultiLingual
        {
            get { return View.IsMultiLingual; }
        }

        protected virtual void PageSizeChanged(object sender, NumericalEventArgs e)
        {
            if (e.Number.HasValue && e.Number.Value != PageSize)
            {
                PageSize = e.Number.Value;
                Bind();
            }
        }

        protected virtual void CultureChanged(object sender, CultureChangedEventArgs e)
        {
            Bind();
        }

        protected virtual void PageIndexChanged(object sender, NumericalEventArgs e)
        {
            if (e.Number.HasValue && e.Number.Value != PageIndex)
            {
                PageIndex = e.Number.Value;
                Bind();
            }
        }

        protected virtual void Rebind(object sender, EventArgs e)
        {
            View.PageIndex = 1;
            Bind();
        }

        protected virtual PagedCollection<TItem> GetData()
        {
            PagedCollection<TItem> data;
            try
            {
                if (View.AllowPaging)
                {
                    data = Repository.PagedWithFilters(IsMultiLingual, CurrentEditionCulture, PageIndex, PageSize,
                (ActiveStatus)Status, BaseTypeId, ParentNodeId, Start.ToStartingComparedDate(),
                End.ToEndingComparedDate(), Keywords, DefaultOrderBy, GetAdditionalWheres(),
               GetAdditionalBetweenAnds(), WithTypeInheritance);
                }
                else
                {
                    var dataWithFilters = Repository.GetWithFilters(IsMultiLingual, CurrentEditionCulture,
                        (ActiveStatus)Status, BaseTypeId, ParentNodeId,
                        Start.ToStartingComparedDate(),
                        End.ToEndingComparedDate(),
                        Keywords, DefaultOrderBy, GetAdditionalWheres(),
                        WithTypeInheritance);

                    data = new PagedCollection<TItem>(dataWithFilters, dataWithFilters.Count);
                }
            }
            catch (BlockingWhereException)
            {
                data = new PagedCollection<TItem>(Enumerable.Empty<TItem>(), 0);
            }
            return data;
        }

        protected virtual string DefaultOrderBy
        {
            get { return View.AllowSorting ? "Position Asc" : null; }
        }

        protected virtual bool WithTypeInheritance
        {
            get { return true; }
        }

        protected virtual IEnumerable<Where> GetAdditionalWheres()
        {
            return Enumerable.Empty<Where>();
        }

        protected virtual IEnumerable<BetweenAnd> GetAdditionalBetweenAnds()
        {
            return Enumerable.Empty<BetweenAnd>();
        }

        protected virtual void ValidateArguments()
        {
            if (PageSize < 1)
                throw new Exception("Page size must be higher than 0.");

            if (Status < 0 || Status > 2)
                throw new Exception("Status must be between 0 and 2.");
        }

        private NodeType _currentType;
        protected virtual NodeType CurrentType
        {
            get { return _currentType ?? (_currentType = NodeTypeManager.GetNodeType(BaseTypeId)); }
        }

        protected virtual void Bind()
        {
            View.EditReturnUrl = "&returnurl=" + EditReturnURL.UrlEncodeBase64();

            ValidateArguments();

            var pagedCollection = GetData();

            View.TotalCount = pagedCollection.TotalCount;
            View.DataSource = pagedCollection.Collection.Cast<object>();
        }

        protected virtual string BuildUrl(string pageUrl, string paramName, Guid nodeId)
        {
            return pageUrl.AddParam(paramName, nodeId)
                .AddParam(DefaultParamNames.ReturnURL, EditReturnURL.UrlEncodeBase64());
        }

        protected virtual string BuildReturnUrl()
        {
            var returlUrl = View.ListingPageUrl;

            if (View.AllowPaging)
            {
                if (PageIndex != 1)
                    returlUrl = returlUrl.AddParam("pageindex", PageIndex);

                if (PageSize != DefaultPageSize)
                    returlUrl = returlUrl.AddParam("pagesize", PageSize);
            }

            if (Status != DefaultStatus)
            {
                returlUrl = returlUrl.AddParam("status", Status);
            }

            if (Keywords != DefaultKeywords)
            {
                returlUrl = returlUrl.AddParam("keywords", Keywords.UrlEncode());
            }

            if (Start != DefaultStart)
            {
                returlUrl = returlUrl.AddParam("start", Start.Value.Ticks);
            }

            if (End != DefaultEnd)
            {
                returlUrl = returlUrl.AddParam("end", End.Value.Ticks);
            }

            if (ParentNodeId.HasValue && ParentNodeId != ModuleNodeId)
            {
                returlUrl = returlUrl
                    .AddParam(DefaultParamNames.ParentNodeID, ParentNodeId)
                    .AddParam(DefaultParamNames.ReturnURL, GetReturnUrlFromQueryString());
            }

            return returlUrl;
        }

        protected virtual string GetReturnUrlFromQueryString()
        {
            return View.QueryString.GetValue<string>(DefaultParamNames.ReturnURL);
        }

        protected override void BindBreadcrumbTrail()
        {
            View.BreadcrumbTrail = GetBreadcrumbsFromQueryString().ToString("<li>", "</li>",
                         "<div class=\"FilAriane\"><ul id=\"FilAriane\" class=\"FilAriane\">",
                         "</ul><br class=\"clearer\"></div>",
                         true, false, null, "<li class=\"DernierItem\">", null, null);
        }

        public class Breadcrumb
        {
            public string Url { get; set; }
            public string Name { get; set; }

            public override string ToString()
            {
                return String.IsNullOrEmpty(Url) ? Name : "<a href=\"" + Url + "\">" + Name + "</a>";
            }
        }

        protected virtual IEnumerable<Breadcrumb> GetBreadcrumbsFromQueryString()
        {
            var rootListingPage = SiteMap.Provider.FindSiteMapNode(View.RootListingPageUrl);
            if (rootListingPage == null)
                throw new Exception("Root listing page not found. Check file name and/or virtual path.");

            if (IsRoot)
            {
                yield return new Breadcrumb { Name = rootListingPage.Title };
            }
            else
            {
                var parents = new List<String> { rootListingPage.Title };
                parents = parents.Union(GetParents()).ToList();
                var returnUrls = GetReturnUrls().Reverse().ToList();
                var lastPagePosition = parents.Count - 1;

                for (var i = 0; i <= lastPagePosition; i++)
                {
                    yield return new Breadcrumb
                    {
                        Name = parents[i],
                        Url = (i == lastPagePosition) ? null : returnUrls[i]
                    };
                }
            }
        }

        protected virtual IEnumerable<string> GetReturnUrls()
        {
            var returnUrl = GetReturnUrlFromQueryString();

            while (!String.IsNullOrEmpty(returnUrl))
            {
                returnUrl = returnUrl.DecodeBase64();
                yield return returnUrl;
                returnUrl = GetReturnUrlFromString(returnUrl);
            }
        }

        protected virtual String GetReturnUrlFromString(string url)
        {
            var uri = new Uri("http://www.google.com" + url);
            return HttpUtility.ParseQueryString(uri.Query).GetValue<string>(DefaultParamNames.ReturnURL);
        }

        protected virtual IList<string> GetParents()
        {
            return ParentNodeId.HasValue ? MedianamikDTOWithNameRepository.ParentsOf(ParentNodeId.Value, true, 0,
                 CurrentUICulture, ContentState.Draft, (ActiveStatus)Status).Select(x => x.Name).ToList() :
                new List<string>();
        }

        #region Status

        private bool _selectedStatusIsRetreived;
        private int? GetSelectedStatus()
        {
            if (View.Status == null)
                return null;

            _selectedStatusIsRetreived = true;
            var selectedTheme = View.Status.FirstOrDefault(i => i.Selected) ??
                                View.Status.FirstOrDefault();

            int? status = null;

            if (selectedTheme != null && !String.IsNullOrEmpty(selectedTheme.Value))
                status = int.Parse(selectedTheme.Value);

            return status;
        }
        private int? _selectedStatus;
        private int? _status;
        protected virtual int? SelectedStatus
        {
            get
            {
                if (_selectedStatusIsRetreived)
                    return _selectedStatus;

                return _selectedStatus = GetSelectedStatus();
            }
        }
        protected virtual void BindStatus(int? selectedStatus)
        {
            var options = new[] { new ListItem("Tous", "2"), new ListItem("Actif", "0"), new ListItem("Inactif", "1") };

            if (selectedStatus.HasValue)
            {
                options.First(i => i.Value.Equals(selectedStatus.ToString())).Selected = true;
            }

            View.Status = options;
        }
        protected virtual int Status
        {
            get
            {
                return _status ?? (_status = GetStatus()).GetValueOrDefault(DefaultStatus);
            }
        }
        protected virtual int? GetStatus()
        {
            return View.IsPostBack ? SelectedStatus : View.QueryString.GetValue<int?>("status");
        }
        protected virtual int DefaultStatus
        {
            get
            {
                return 2;
            }
        }

        #endregion

        protected virtual CultureInfo CurrentEditionCulture
        {
            get
            {
                return View.CurrentEditionCulture;
            }
        }

        protected virtual CultureInfo CurrentUICulture
        {
            get
            {
                return View.CurrentUICulture;
            }
        }

        private string _editReturnURL;
        protected string EditReturnURL
        {
            get
            {
                return _editReturnURL ?? (_editReturnURL = BuildReturnUrl());
            }
        }

        protected virtual string FormatChildItemsCount(int childCount)
        {
            return childCount > 0 ? "&nbsp;(" + childCount + (childCount > 1 ? "&nbsp;items)" : "&nbsp;item)") : "";
        }
    }
}